package com.atguigu.spring.aop.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.Executors;

/**
 * 1.在切面中，需要通过指定的注解将方法表标识为通知方法
 * @Before:在目标对象方法执行前执行
 * @After:在目标对象的finally字句中执行
 * @AfterReturning:在目标对象方法返回之后执行
 * @AfterThrowing:异常通知，在目标对象方法的 catth 字句中执行
 * 2.切入点表达式：
 * @Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int,int))")
 * @Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
 * 第一个*表示任意的访问修饰符和返回值类型
 * 第二个*表示类中任意的方法
 * ..表示任意的参数列表
 * 类的地方也可以使用*，表示包下所有的类
 * 3.重用切入点表达式
 * //@Pointcut声明一个公共的切入点表达式
 * @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
 * public void pointCut(){}
 * 使用方式：@Before("pointCut()")
 * 4.获得连接点信息
 * 在通知方法的参数位置，设置JoinPoint类型的参数，就可以获取连接点对应方法的信息
 * //获取连接点对应方法的签名信息
 * joinPoint.getSignature().getName();
 * //获取连接点对应方法的参数列表
 * Object[] args = joinPoint.getArgs();
 * 5.切面的优先级
 * @Order(整数) 整数越小，优先级越高   最大值 2147483647
 *
 */
@Component  //标识为普通组件
@Aspect //将当前组件标识为切面组件
public class LogAspect {
    //定义切点
    @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut(){
    }

    //@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int,int))")
    @Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置通知，方法名："+name+",参数是："+ Arrays.toString(args));
    }
    @After("pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->后置通知，方法名："+name+",参数是："+ Arrays.toString(args));
    }
    //在返回通知中，若要获取目标对象方法的返回值，只需要同@AfterReturning注解的returning属性，
    // 就可以将通知方法的某个参数指定为接受目标对象的方法的返回值的参数
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->返回通知,方法名："+name+",参数是："+ Arrays.toString(args)+",结果是："+result);
    }
    //在异常通知中，若要获取目标对象方法的异常，只需要同@AfterThrowing注解的throwing属性，
    // 就可以将通知方法的某个参数指定为接受目标对象的方法的异常的参数
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->异常通知,方法名："+name+",参数是："+ Arrays.toString(args)+",异常信息是："+ex);
    }


    @Around("pointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Object result=null;
        try{
            System.out.println("环绕通知：前置通知");
            result = joinPoint.proceed();
            System.out.println("环绕通知：返回通知");
        }catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知：异常通知");
        } finally {
            System.out.println("环绕通知：后置通知");
        }
        return result;
    }
}
